package com.tanqidi.arcgisandroid.ui.projectConfig

import android.os.Environment
import androidx.lifecycle.ViewModel
import com.esri.arcgisruntime.data.Field
import com.esri.arcgisruntime.data.FieldDescription
import com.esri.arcgisruntime.data.Geodatabase
import com.esri.arcgisruntime.data.TableDescription
import com.esri.arcgisruntime.geometry.GeometryType
import com.esri.arcgisruntime.geometry.SpatialReference
import com.tanqidi.arcgisandroid.R
import com.tanqidi.arcgisandroid.base.diyJetpack.BusMutableLiveData
import com.tanqidi.arcgisandroid.base.utils.getString
import com.tanqidi.arcgisandroid.base.utils.showToast
import com.tanqidi.arcgisandroid.data.layerConfig.LayerConfig.Companion.TYPE_POINT
import com.tanqidi.arcgisandroid.data.layerConfig.LayerConfig.Companion.TYPE_POLYGON
import com.tanqidi.arcgisandroid.data.layerConfig.LayerConfig.Companion.TYPE_POLYLINE
import com.tanqidi.arcgisandroid.data.project.Project
import com.tanqidi.arcgisandroid.data.project.ProjectRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import io.reactivex.rxjava3.core.Single
import org.gdal.gdal.gdal
import org.gdal.ogr.FieldDefn
import org.gdal.ogr.ogr
import java.io.File
import java.util.*
import javax.inject.Inject

@HiltViewModel
class ProjectConfigViewModel @Inject constructor(
    private val projectRepository: ProjectRepository
) : ViewModel() {

    fun saveProject(project: Project) {
        projectRepository.saveProject(project)
    }

    val listProjectLiveData = BusMutableLiveData<List<Project>>()
    fun listProject() {
        val listProject = projectRepository.listProject()
        listProjectLiveData.postValue(listProject)
    }

    /**
     * 根据项目配置，生成shp文件
     */
    fun createShp(project: Project) {
        //这里是  ArcgisAndroid/项目数据/shp
        val fileFolder = Environment.getExternalStorageDirectory().absolutePath + File.separator + getString(R.string.app_name) + File.separator + getString(R.string.project_data) + File.separator + getString(R.string.shp)
        val projectShpFolder = File(fileFolder, project.name)
        projectShpFolder.mkdirs()
        //在这个目录中创建项目目录 ArcgisAndroid/项目数据/shp/项目名字

        // 注册所有的驱动
        ogr.RegisterAll()
        // 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8","YES")
        // 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING","")
        //创建Driver
        val driver = ogr.GetDriverByName("ESRI Shapefile")

        //创建gdal的坐标系对象
        val spRefence = org.gdal.osr.SpatialReference(SpatialReference.create(4326).wkText)
        //根据ServiceFeatureTable类型创建gdal图层
        val vector = Vector<String>()
        vector.addElement("ENCODING=UTF-8")
        project.layers.forEachIndexed { index, layer ->
            //在这里创建具体的shp
            val shpPath = File(projectShpFolder, layer.name)
            shpPath.mkdirs()
            //得到dataSource
            val createDataSource = driver.CreateDataSource(shpPath.absolutePath, null)
            //创建图层
            val shapeLayer = when(layer.type){
                TYPE_POINT -> {
                    createDataSource.CreateLayer(layer.name, spRefence, ogr.wkbPoint, vector)
                }
                TYPE_POLYLINE -> {
                    createDataSource.CreateLayer(layer.name, spRefence, ogr.wkbLineString, vector)
                }
                TYPE_POLYGON -> {
                    createDataSource.CreateLayer(layer.name, spRefence, ogr.wkbPolygon, vector)
                }
                else -> {
                    createDataSource.CreateLayer(layer.name, spRefence, ogr.wkbUnknown, vector)
                }
            }
            //创建字段
            layer.fields.forEachIndexed { index, field ->
                val fieldDefn = FieldDefn(field.field, ogr.OFTString)
                shapeLayer.CreateField(fieldDefn)
            }
            //保存
            shapeLayer.SyncToDisk()
            shapeLayer.delete()
        }

        showToast("创建成功")
    }

    fun createGeodatabase(project: Project) {
        //这里是  ArcgisAndroid/项目数据/geodatabase
        val fileFolder = Environment.getExternalStorageDirectory().absolutePath + File.separator + getString(R.string.app_name) + File.separator + getString(R.string.project_data) + File.separator + getString(R.string.geodatabase)
        val createAsync = Geodatabase.createAsync(fileFolder + File.separator + project.name + ".geodatabase")
        createAsync.addDoneListener {
            val geodatabase = createAsync.get()
            val asyncJobList = mutableListOf<Single<Any>>()
            project.layers.forEachIndexed { index, layer ->
                val geometryType = when (layer.type) {
                    TYPE_POINT -> GeometryType.POINT
                    TYPE_POLYLINE -> GeometryType.POLYLINE
                    TYPE_POLYGON -> GeometryType.POLYGON
                    else -> GeometryType.POINT
                }

                /**
                 * 数据表嘛，我统一口径用4326坐标系这一点你应该可以看到我用在了很多地方，无论是geojson还是shp还是geodatabase我都统一了，你也可以换成其他的
                 * 就连后面的featureServer我也会用4326坐标系
                 */
                val tableDescription = TableDescription(layer.name, SpatialReference.create(4326), geometryType)
                /**
                 * 重要，开启存储附件功能
                 */
                tableDescription.setHasAttachments(true)
                layer.fields.forEachIndexed { index, field ->
                    //创建字段和类型，我这里全部都用TEXT，就是String的意思，不做其他格式的了你可以改成其他的也行
                    val fieldDescription = FieldDescription(field.field, Field.Type.TEXT)
                    fieldDescription.length = 64
                    tableDescription.fieldDescriptions.add(fieldDescription)
                }
                val job = Single.create<Any> { emit->
                    val createTableAsync = geodatabase.createTableAsync(tableDescription)
                    createTableAsync.addDoneListener {
                        try {
                            val get = createTableAsync.get()
                            get.hasAttachments()
                            emit.onSuccess(true)
                        } catch (e: Exception) {
                            e.printStackTrace()
                            emit.onError(e)
                        }
                    }
                }
                asyncJobList.add(job)
            }
            //全部异步任务完成后再添加进来
            Single.zip(asyncJobList) { objects -> objects }.subscribe({
               showToast("创建成功")
            }, {
               showToast("创建失败")
            })
        }
    }

    fun deleteProject(project: Project) {
        projectRepository.deleteProject(project)
    }

}